/*
 * Copyright (c) 2006-2010 Sun Microsystems, Inc.
 * Copyright (c) 2010 Oracle
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package de.hsrm;

import com.sun.spot.resources.Resources;
import com.sun.spot.resources.transducers.ISwitch;
import com.sun.spot.resources.transducers.ISwitchListener;
import com.sun.spot.resources.transducers.ITriColorLEDArray;
import com.sun.spot.resources.transducers.SwitchEvent;
import com.sun.spot.io.j2me.radiogram.*;

import com.sun.spot.peripheral.radio.RadioFactory;
import com.sun.spot.resources.transducers.LEDColor;
import com.sun.spot.service.BootloaderListenerService;
import com.sun.spot.util.IEEEAddress;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

/**
 * The startApp method of this class is called by the VM to start the
 * application.
 *
 * The manifest specifies this class as MIDlet-1, which means it will
 * be selected for execution.
 */
public class CoordinatorMidlet extends MIDlet implements ISwitchListener{

    private ITriColorLEDArray leds = (ITriColorLEDArray) Resources.lookup(ITriColorLEDArray.class);
//    private RadiogramConnection tx = null;
//    private Radiogram xdg;
    private int myAddr = Address.COORDINATOR;
    private final int SEQNR_BUFFERSIZE = 100;
    public final int MAX_ADDRESSES = Util.MAX_ADRESSES;
    private int[] sentSeqBuffer = new int[SEQNR_BUFFERSIZE];
    private int sentSeqBufferIndex = 0;
    private int lastMsgSeqNr = -2;
    private int availableAddr = 2;
    private boolean threadON = false;
    RadiogramConnection tx = null;
    Radiogram xdg;
    private CoordinatorSendThread checkCarsThread;
    
    
    private ISwitch sw1 = (ISwitch)Resources.lookup(ISwitch.class, "SW1");
    private ISwitch sw2 = (ISwitch)Resources.lookup(ISwitch.class, "SW2");
    
    protected void startApp() throws MIDletStateChangeException {
        BootloaderListenerService.getInstance().start();   // monitor the USB (if connected) and recognize commands from host
        sw1.addISwitchListener(this);
        sw2.addISwitchListener(this);
        leds.setColor(LEDColor.RED);
        leds.setOn();
        
        long ourAddr = RadioFactory.getRadioPolicyManager().getIEEEAddress();
        System.out.println("Our radio address = " + IEEEAddress.toDottedHex(ourAddr));
        
                 
        
        try {
            tx = (RadiogramConnection)Connector.open("radiogram://broadcast:123");
            xdg = (Radiogram)tx.newDatagram(tx.getMaximumLength());
            
            RadiogramConnection rx = (RadiogramConnection)Connector.open("radiogram://:123");
            Radiogram rdg = (Radiogram)rx.newDatagram(rx.getMaximumLength());
            while (true) {
                try {
                    rx.receive(rdg);
                    evaluateIncomingMessage(rdg);                   
                    
                } catch (IOException ex) {
                    System.out.println("Error receiving packet: " + ex);
                    ex.printStackTrace();
                }
            }
        } catch (IOException ex) {
            System.out.println("Error opening connections: " + ex);
            ex.printStackTrace();
        }                     // cause the MIDlet to exit
    }
    
    protected void pauseApp() {
        // This is not currently called by the Squawk VM
    }
    
    /**
     * Called if the MIDlet is terminated by the system.
     * I.e. if startApp throws any exception other than MIDletStateChangeException,
     * if the isolate running the MIDlet is killed with Isolate.exit(), or
     * if VM.stopVM() is called.
     * 
     * It is not called if MIDlet.notifyDestroyed() was called.
     *
     * @param unconditional If true when this method is called, the MIDlet must
     *    cleanup and release all resources. If false the MIDlet may throw
     *    MIDletStateChangeException  to indicate it does not want to be destroyed
     *    at this time.
     */
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
        leds.setOff();
        
    }
    
    protected void evaluateIncomingMessage(Radiogram incomingRdg)
    {
        try {
                int sourceAddr = incomingRdg.readInt();
                int destAddr = incomingRdg.readInt();
                int msgType = incomingRdg.readInt();
                int msgSeqNr = incomingRdg.readInt();
                int assignedAddr = incomingRdg.readInt();
                int data = incomingRdg.readInt();
                long sourceMacAddress = incomingRdg.getAddressAsLong();
                
                
                if(!checkSeqNr(msgSeqNr))
                { 
                    if (destAddr != myAddr)
                    {
                        if(msgSeqNr == lastMsgSeqNr + 1)
                        {
                            if(msgType == MsgType.SENDADDR)
                            {
                                //myAddr = assignedAddr;
                                //sendMessage(myAddr, sourceAddr, MsgType.ACK, 0, 0);
                            }

                        }
                        else 
                        {
                            
//                            System.out.println("\nMessage not for me received and sent forward:\n"
//                            + "src: " + String.valueOf(sourceAddr) 
//                            + "\ndest: " + String.valueOf(destAddr)
//                            + "\ntype: " + String.valueOf(msgType)
//                            + "\nSeqNr: " + String.valueOf(msgSeqNr)
//                            + "\nassigned Address: " + String.valueOf(assignedAddr));
                            sendMessage(sourceAddr, destAddr, msgType, msgSeqNr, assignedAddr, data);
                        }
                    }
                    else    //Message is for me
                    {
                        switch(msgType)
                        {
                            case MsgType.GETADDR:
                                
                                sendMessage(myAddr, Address.UNKNOWN, MsgType.SENDADDR, msgSeqNr + 1,
                                        AddressTable.getInstance().generateAddress(data), 0);
                                System.out.println("\nAddress request received:\n"
                                + "src: " + String.valueOf(sourceAddr) 
                                + "\ndest: " + String.valueOf(destAddr)
                                + "\ntype: " + String.valueOf(msgType)
                                + "\nSeqNr: " + String.valueOf(msgSeqNr)
                                + "\nassigned Address: " + String.valueOf(assignedAddr));
                                break;                            
                            case MsgType.ACK:
                                System.out.println("\nAddress Acknowledge received:\n"
                                + "src: " + String.valueOf(sourceAddr) 
                                + "\ndest: " + String.valueOf(destAddr)
                                + "\ntype: " + String.valueOf(msgType)
                                + "\nSeqNr: " + String.valueOf(msgSeqNr)
                                + "\nassigned Address: " + String.valueOf(assignedAddr)
                                 +"\nmacAdress: " + String.valueOf(sourceMacAddress));
                                AddressTable.getInstance().addAddressTableEntry(sourceAddr, sourceMacAddress);
                                //addressTable[1].addElement(String.valueOf(sourceAddr));
                                break;
                            case MsgType.CHECK:
                                System.out.println("\nCar checked:\n"
                                + "src: " + String.valueOf(sourceAddr) 
                                + "\ndest: " + String.valueOf(destAddr)
                                + "\ntype: " + String.valueOf(msgType)
                                + "\nSeqNr: " + String.valueOf(msgSeqNr));
                                AddressTable.getInstance().updateAddressTableEntry(sourceAddr, sourceMacAddress, true);
                                break;
//                            case MsgType.SIGNAL:
//                                AddressTable.getInstance().deleteAddressTableEntry(sourceAddr);
//                                System.out.println("\nAddress deleted:\n"
//                                + "src: " + String.valueOf(sourceAddr));
//                                break;
                        }

                    }
                }
                
            } catch (IOException ex) {
                    System.out.println("Error evaluating packet: " + ex);
                    ex.printStackTrace();
            }
    }
    
    public void switchReleased(SwitchEvent evt)
    {
        if (evt.getSwitch() == sw1)
        {
            System.out.println("Address Table:");
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    for (int k = 1; k < Util.MAX_ADRESSES; k++)
                    {
                        if(AddressTable.getInstance().getCar((10000 + (i*1000) +(j*100) + k)) != null)
                        {
                            System.out.println("Address: "
                                    + String.valueOf(AddressTable.getInstance().getCar((10000 + (i*1000) +(j*100) + k)).getAddress())
                                    + "   Mac: "
                                    + String.valueOf(AddressTable.getInstance().getCar((10000 + (i*1000) +(j*100) + k)).getMacAddress())
                                    + " WaitTime: "
                                    + String.valueOf(AddressTable.getInstance().getCar((10000 + (i*1000) +(j*100) + k)).getWaitTime())
                                    );
                        }
                    }   
                }
            }
        }
        else if (!threadON)
        {
            checkCarsThread = new CoordinatorSendThread();
            checkCarsThread.threadON = true;
            checkCarsThread.start();
            threadON = true;
            leds.setColor(LEDColor.GREEN);
            leds.setOn();
        }
        else
        {
            checkCarsThread.threadON = false;
            threadON = false;
            AddressTable.getInstance().resetTable();
            leds.setColor(LEDColor.RED);
            leds.setOn();
        }
    }
    

    public void switchPressed(SwitchEvent evt) {
    }
        
    protected void sendMessage(int msgSource, int msgDest, int msgType, int msgSeqNr, int assignedAddr, int data)
    {
        try {
                      
            xdg.reset();
            
            xdg.writeInt(msgSource);
            xdg.writeInt(msgDest);
            xdg.writeInt(msgType);
            xdg.writeInt(msgSeqNr);
            xdg.writeInt(assignedAddr);
            xdg.writeInt(data);
            tx.send(xdg);
            
            if(msgType == MsgType.SENDADDR)
            {    
                System.out.println("\nAddress Request replied:\n"
                        + "src: " + String.valueOf(msgSource) 
                        + "\ndest: " + String.valueOf(msgDest)
                        + "\ntype: " + String.valueOf(msgType)
                        + "\nSeqNr: " + String.valueOf(msgSeqNr)
                        + "\nassigned Address: " + String.valueOf(assignedAddr));
            }
            
            } catch (IOException ex){
            System.out.println("Error sending packet: " + ex);
            ex.printStackTrace();
        }
    }
    
    protected boolean checkSeqNr(int SeqNr)
    {
        for (int i = 0; i < SEQNR_BUFFERSIZE; i++)
        {
            if (sentSeqBuffer[i] == SeqNr)
                return true;
        }
        
        sentSeqBuffer[sentSeqBufferIndex%SEQNR_BUFFERSIZE] = SeqNr;
        sentSeqBufferIndex++;
        return false;
    }
    
    protected boolean checkAddressTableEntry(int address, long macAddress)
    {
        if(AddressTable.getInstance().getCar(address).getMacAddress() == macAddress)
            return true;
        else
            return false;
    }
     
     
}